﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ShevaEngine.Core.Modules.Graphics;
using ShevaEngine.Core.Modules.Materials;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ShevaEngine.Core.Interfaces;

namespace ShevaEngine.Core.Foundation.GraphicsObjects
{
    /// <summary>
    /// Drawing class.
    /// </summary>
    public class Primitives
    {
        /// <summary>Singleton.</summary>
        public static readonly Primitives Instance = new Primitives();

        /// <summary>Circle segments count.</summary>
        public const Int32 CircleSegmentsCount = 25;

        /// <summary>Material.</summary>
        public DrawCallKey DrawCallKey;

        /// <summary>Circle draw call.</summary>
        public UserPrimitivesDrawCall<VertexPositionNormal> BoxDrawCallFill;
        /// <summary>Circle draw call.</summary>
        public UserPrimitivesDrawCall<VertexPositionNormal> BoxDrawCallBorder;

        /// <summary>Circle draw call.</summary>
        public UserPrimitivesDrawCall<VertexPositionNormal> CylinderDrawCallFill;
        /// <summary>Circle draw call.</summary>
        public UserPrimitivesDrawCall<VertexPositionNormal> CylinderDrawCallBorder;

        /// <summary>
        /// Konstruktor.
        /// </summary>
        private Primitives()
        {
            this.DrawCallKey = new
                DrawCallKey(
                    MaterialManager.Instance.GetMaterialID("MaterialBaseVertexColor"),
                    DrawCallKey.None,
                    DrawCallKey.None,
                    DrawCallKey.None);
            
            List<VertexPositionNormal> verticesTempFill = new List<VertexPositionNormal>();
            List<VertexPositionNormal> verticesTempBorder = new List<VertexPositionNormal>();

            // A cube has six faces, each one pointing in a different direction.
            Vector3[] normals = { Vector3.UnitZ, -Vector3.UnitZ, Vector3.UnitX, -Vector3.UnitX, Vector3.UnitY, -Vector3.UnitY };


            // Create each face in turn.
            foreach (Vector3 normal in normals)
            {
                // Get two vectors perpendicular to the face normal and to each other.
                Vector3 side1 = new Vector3(normal.Y, normal.Z, normal.X);
                Vector3 side2 = Vector3.Cross(normal, side1);

                // Six indices (two triangles) per face.
                verticesTempFill.Add(new VertexPositionNormal(normal - side1 - side2, normal));
                verticesTempFill.Add(new VertexPositionNormal(normal - side1 + side2, normal));
                verticesTempFill.Add(new VertexPositionNormal(normal + side1 + side2, normal));

                verticesTempFill.Add(new VertexPositionNormal(normal - side1 - side2, normal));
                verticesTempFill.Add(new VertexPositionNormal(normal + side1 + side2, normal));
                verticesTempFill.Add(new VertexPositionNormal(normal + side1 - side2, normal));


                verticesTempBorder.Add(new VertexPositionNormal(normal - side1 - side2, normal));
                verticesTempBorder.Add(new VertexPositionNormal(normal - side1 + side2, normal));
                verticesTempBorder.Add(new VertexPositionNormal(normal - side1 + side2, normal));
                verticesTempBorder.Add(new VertexPositionNormal(normal + side1 + side2, normal));
                verticesTempBorder.Add(new VertexPositionNormal(normal + side1 + side2, normal));
                verticesTempBorder.Add(new VertexPositionNormal(normal + side1 - side2, normal));
                verticesTempBorder.Add(new VertexPositionNormal(normal + side1 - side2, normal));
                verticesTempBorder.Add(new VertexPositionNormal(normal - side1 - side2, normal));
            }

            this.BoxDrawCallFill = new UserPrimitivesDrawCall<VertexPositionNormal>(
                PrimitiveType.TriangleList, verticesTempFill.ToArray(), 12);

            this.BoxDrawCallBorder = new UserPrimitivesDrawCall<VertexPositionNormal>(
                PrimitiveType.LineList, verticesTempBorder.ToArray(), verticesTempBorder.Count / 2);                       

            verticesTempFill = new List<VertexPositionNormal>();
            verticesTempBorder = new List<VertexPositionNormal>();

            /// Bottom
            for (int i = 0; i < Drawing.CircleSegmentsCount; i++)
            {
                verticesTempBorder.Add(new VertexPositionNormal(
                    new Vector3((float)Math.Sin((i * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1))),
                        -1, (float)Math.Cos((i * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1)))), -Vector3.UnitY));

                verticesTempBorder.Add(new VertexPositionNormal(
                    new Vector3((float)Math.Sin(((i + 1) * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1))),
                        -1, (float)Math.Cos(((i + 1) * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1)))), -Vector3.UnitY));

                verticesTempBorder.Add(new VertexPositionNormal(
                    new Vector3((float)Math.Sin(((i + 1) * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1))),
                        -1, (float)Math.Cos(((i + 1) * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1)))), -Vector3.UnitY));

                verticesTempBorder.Add(new VertexPositionNormal(new Vector3(0, -1, 0), -Vector3.UnitY));
            }

            /// Top
            for (int i = 0; i < Drawing.CircleSegmentsCount; i++)
            {
                verticesTempBorder.Add(new VertexPositionNormal(
                    new Vector3((float)Math.Sin((i * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1))),
                        1, (float)Math.Cos((i * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1)))), Vector3.UnitY));

                verticesTempBorder.Add(new VertexPositionNormal(
                    new Vector3((float)Math.Sin(((i + 1) * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1))),
                        1, (float)Math.Cos(((i + 1) * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1)))), Vector3.UnitY));

                verticesTempBorder.Add(new VertexPositionNormal(
                    new Vector3((float)Math.Sin((i * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1))),
                        1, (float)Math.Cos((i * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1)))), Vector3.UnitY));

                verticesTempBorder.Add(new VertexPositionNormal(new Vector3(0, 1, 0), Vector3.UnitY));
            }

            /// Side
            for (int i = 0; i < Drawing.CircleSegmentsCount; i++)
            {
                verticesTempBorder.Add(new VertexPositionNormal(
                    new Vector3((float)Math.Sin((i * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1))),
                        1, (float)Math.Cos((i * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1)))), Vector3.UnitY));

                verticesTempBorder.Add(new VertexPositionNormal(
                    new Vector3((float)Math.Sin((i * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1))),
                        -1, (float)Math.Cos((i * (MathHelper.Pi * 2.0f) / (Primitives.CircleSegmentsCount - 1)))), Vector3.UnitZ));
            }

            this.CylinderDrawCallFill = new UserPrimitivesDrawCall<VertexPositionNormal>(
                PrimitiveType.TriangleList, verticesTempFill.ToArray(), 12);

            this.CylinderDrawCallBorder = new UserPrimitivesDrawCall<VertexPositionNormal>(
                PrimitiveType.LineList, verticesTempBorder.ToArray(), verticesTempBorder.Count / 2);
        }

        /// <summary>
        /// Method creates rectangle.
        /// </summary>
        /// <param name="pipeline"></param>
        /// <param name="centerPosition"></param>
        /// <param name="color"></param>
        public DrawCall CreateBoxFill()
        {
            return this.BoxDrawCallFill;
        }

        /// <summary>
        /// Method creates rectangle.
        /// </summary>
        /// <param name="pipeline"></param>
        /// <param name="centerPosition"></param>
        /// <param name="color"></param>
        public DrawCall CreateBoxBorder()
        {
            return this.BoxDrawCallBorder;
        }

        /// <summary>
        /// Method creates Cylinder.
        /// </summary>
        /// <param name="pipeline"></param>
        /// <param name="centerPosition"></param>
        /// <param name="color"></param>
        public DrawCall CreateCylinderFill()
        {
            return this.CylinderDrawCallFill;
        }

        /// <summary>
        /// Method creates Cylinder.
        /// </summary>
        /// <param name="pipeline"></param>
        /// <param name="centerPosition"></param>
        /// <param name="color"></param>
        public DrawCall CreateCylinderBorder()
        {
            return this.CylinderDrawCallBorder;
        }
    }
}
